#include "window.h"
#include "debug/logger.h"
#include "graphics/d3d/d3ddevice.h"

namespace renderit {
	namespace client {

#define RENDERIT_WND_CLZ	"RenderItWndClz"

		window::window()
			: wnd_(0)
			, width_(0)
			, height_(0)
		{

		}

		window::~window()
		{
			if (wnd_)
			{
				::CloseWindow(wnd_);
				wnd_ = 0;
			}
		}

		window * window::create(uint32 width, uint32 height, const std::string& title)
		{
			window::register_class();
			HWND wnd = window::create_window(width, height, title);
			window * w = new window();
			w->wnd_ = wnd;
			w->width_ = width;
			w->height_ = height;
			w->title_ = title;
			w->device_ = new graphics::d3ddevice(w);

			::SetWindowLongPtr(wnd, 0, (LONG_PTR)w);

			return w;
		}

		BOOL window::register_class()
		{
			static bool first_time = true;
			if (!first_time) {
				return true;
			}
			first_time = false;

			HINSTANCE hInstance = ::GetModuleHandle(NULL);
			WNDCLASSEX wcx;

			wcx.cbSize = sizeof(wcx);
			wcx.style = CS_DBLCLKS |
				CS_DROPSHADOW |
				CS_HREDRAW |
				CS_VREDRAW;
			wcx.lpfnWndProc = window::wnd_proc;
			wcx.cbClsExtra = 0;
			wcx.cbWndExtra = sizeof(window*);
			wcx.hInstance = hInstance;
			wcx.hIcon = ::LoadIcon(hInstance, IDI_APPLICATION);
			wcx.hCursor = ::LoadCursor(hInstance, IDC_ARROW);
			wcx.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
			wcx.lpszMenuName = NULL;
			wcx.lpszClassName = RENDERIT_WND_CLZ;
			wcx.hIconSm = (HICON)::LoadImage(hInstance, MAKEINTRESOURCE(1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), LR_DEFAULTCOLOR);
			return ::RegisterClassEx(&wcx);
		}

		HWND window::create_window(uint32 width, uint32 height, const std::string& title)
		{
			HINSTANCE hInstance = ::GetModuleHandle(NULL);
			HWND wnd = ::CreateWindow(
				RENDERIT_WND_CLZ,
				title.c_str(),
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				width,
				height,
				NULL,
				NULL,
				hInstance,
				(LPVOID)NULL
			);

			return wnd;
		}

		LRESULT CALLBACK window::wnd_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
		{
			window * w = (window*)::GetWindowLongPtr(wnd, 0);

			//logger::debug("WndProc:%p, %d, %d, %d", w, msg, wparam, lparam);

			input_event ievent;

			switch (msg) {
			case WM_DESTROY:
				::PostQuitMessage(0);
				break;

			case WM_MOUSEMOVE:
				ievent.event = EInputEvent_MouseMove;
				ievent.u.mouse.x = LOWORD(lparam);
				ievent.u.mouse.y = HIWORD(lparam);
				break;

			case WM_LBUTTONDOWN:
				ievent.event = EInputEvent_LMouseDown;
				ievent.u.mouse.x = LOWORD(lparam);
				ievent.u.mouse.y = HIWORD(lparam);
				break;

			case WM_LBUTTONUP:
				ievent.event = EInputEvent_LMouseUp;
				ievent.u.mouse.x = LOWORD(lparam);
				ievent.u.mouse.y = HIWORD(lparam);
				break;

			case WM_RBUTTONDOWN:
				ievent.event = EInputEvent_RMouseDown;
				ievent.u.mouse.x = LOWORD(lparam);
				ievent.u.mouse.y = HIWORD(lparam);
				break;
				
			case WM_RBUTTONUP:
				ievent.event = EInputEvent_RMouseUp;
				ievent.u.mouse.x = LOWORD(lparam);
				ievent.u.mouse.y = HIWORD(lparam);
				break;

			case WM_KEYDOWN:
				ievent.event = EInputEvent_KeyDown;
				ievent.u.key.vk = wparam;
				break;

			case WM_KEYUP:
				ievent.event = EInputEvent_KeyUp;
				ievent.u.key.vk = wparam;
				break;
			}

			if (ievent.event != EInputEvent_None)
			{
				if (w->listener_)
				{
					w->listener_(ievent);
				}
			}

			return ::DefWindowProc(wnd, msg, wparam, lparam);
		}

		void window::show(bool b)
		{
			if (wnd_) {
				::ShowWindow(wnd_, b);
				::UpdateWindow(wnd_);
			}
		}

		bool window::loop()
		{
			MSG msg;
			if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
				if (msg.message == WM_QUIT) {
					return false;
				}

				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
			return true;
		}
	}
} // namespace renderit